package com.pai4j.zwiki.service;

import lombok.extern.slf4j.Slf4j;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * Git仓库操作服务
 * 负责从GitHub克隆代码仓库
 *
 * @author pai4j
 * @since 2025-10-12
 */
@Slf4j
@Service
public class GitRepositoryService {

    @Value("${zwiki.workspace.base-path:/tmp/zwiki-workspace}")
    private String workspaceBasePath;

    /**
     * 克隆GitHub仓库
     *
     * @param repositoryUrl GitHub仓库URL
     * @param taskId        任务ID
     * @return 本地仓库路径
     */
    public String cloneRepository(String repositoryUrl, String taskId) {
        log.info("开始克隆仓库: {}, 任务ID: {}", repositoryUrl, taskId);
        
        Git git = null;
        try {
            // 创建工作目录
            Path workspacePath = Paths.get(workspaceBasePath, taskId);
            Files.createDirectories(workspacePath);

            // 克隆仓库（使用 try-with-resources 确保资源正确释放）
            git = Git.cloneRepository()
                    .setURI(repositoryUrl)
                    .setDirectory(workspacePath.toFile())
                    .setCloneAllBranches(false)
                    .call();

            log.info("仓库克隆成功: {}", workspacePath);
            return workspacePath.toString();
            
        } catch (GitAPIException | IOException e) {
            log.error("克隆仓库失败: {}", repositoryUrl, e);
            throw new RuntimeException("克隆仓库失败: " + e.getMessage(), e);
        } finally {
            // 确保 Git 资源被正确释放
            if (git != null) {
                git.close();
            }
        }
    }

    /**
     * 验证仓库URL格式
     */
    public boolean isValidRepositoryUrl(String url) {
        if (url == null || url.trim().isEmpty()) {
            return false;
        }
        return url.matches("^https?://github\\.com/[\\w-]+/[\\w-]+(\\.git)?$");
    }

    /**
     * 从URL提取仓库名称
     */
    public String extractRepositoryName(String url) {
        if (url == null) {
            return "unknown";
        }
        String[] parts = url.replace(".git", "").split("/");
        return parts.length > 0 ? parts[parts.length - 1] : "unknown";
    }

    /**
     * 清理工作目录（优化版 - 使用 NIO 和安全检查）
     */
    public void cleanupWorkspace(String workspacePath) {
        if (workspacePath == null || workspacePath.trim().isEmpty()) {
            log.warn("工作目录路径为空，跳过清理");
            return;
        }
        
        try {
            Path workspace = Paths.get(workspacePath);
            
            // 安全检查：确保只删除工作目录下的文件
            Path baseWorkspace = Paths.get(workspaceBasePath);
            if (!workspace.normalize().startsWith(baseWorkspace.normalize())) {
                log.error("安全检查失败：尝试删除工作目录之外的路径: {}", workspacePath);
                throw new SecurityException("禁止删除工作目录之外的路径");
            }
            
            if (Files.exists(workspace)) {
                deleteDirectorySafely(workspace);
                log.info("工作目录清理完成: {}", workspacePath);
            }
        } catch (Exception e) {
            log.error("清理工作目录失败: {}", workspacePath, e);
            // 清理失败不应该中断业务流程，只记录错误
        }
    }

    /**
     * 递归删除目录（使用 NIO，更安全和高效）
     */
    private void deleteDirectorySafely(Path directory) throws IOException {
        if (!Files.exists(directory)) {
            return;
        }
        
        try (var stream = Files.walk(directory)) {
            stream.sorted((p1, p2) -> -p1.compareTo(p2)) // 逆序，先删除子文件/目录
                  .forEach(path -> {
                      try {
                          Files.deleteIfExists(path);
                      } catch (IOException e) {
                          log.warn("删除文件失败: {}", path, e);
                      }
                  });
        }
    }
}

